package isjinhao.jlox.grammar.code;

import isjinhao.jlox.grammar.Parser;
import isjinhao.jlox.grammar.code.represention.*;
import isjinhao.jlox.grammar.expression.ExpressionParser;
import isjinhao.jlox.grammar.expression.represention.Expression;
import isjinhao.jlox.grammar.statement.StatementParser;
import isjinhao.jlox.grammar.statement.represention.Statement;
import isjinhao.jlox.lexical.TokenSequence;
import isjinhao.jlox.lexical.TokenType;

import java.util.ArrayList;
import java.util.List;

/**
 * @Author ISJINHAO
 * @Date 2021/2/21 10:36
 */
public class CodeParser extends Parser<List<Code>> {

    protected StatementParser statementParser;
    protected ExpressionParser expressionParser;

    public CodeParser(TokenSequence tokenSequence) {
        super(tokenSequence);
        statementParser = new StatementParser(tokenSequence);
        expressionParser = new ExpressionParser(tokenSequence);
    }

    /**
     * if               ->  "if" "(" expression ")" statement
     * ("elseif" "(" statement ")")*
     * ("else" statement)?
     * while            ->  "while" "(" expression ")" statement
     * for              ->  "for" "("  (varDefinition | assignment) ";" expression ";" assignment ")" statement
     * do_while         ->  "do" statement "while" "(" expression ")"
     * code             ->  (statement | if | while | for | do_while)*
     */
    @Override
    public List<Code> parse() {
        return code();
    }

    private List<Code> code() {
        ArrayList<Code> codes = new ArrayList<>();
        while (!tokenSequence.isAtEnd()) {
            try {

                if (tokenSequence.curEqual(TokenType.IF)) {
                    codes.add(ifCode());
                } else if (tokenSequence.curEqual(TokenType.WHILE)) {
                    codes.add(whileCode());
                } else if (tokenSequence.curEqual(TokenType.FOR)) {
                    codes.add(forCode());
                } else if (tokenSequence.curEqual(TokenType.DO)) {
                    codes.add(doWhileCode());
                } else {
                    codes.add(statementCode());
                }
            } catch (Exception e) {
                synchronize();
            }
        }
        return codes;
    }

    private Code doWhileCode() {

        tokenSequence.advance();

        Statement statement = statementParser.parse();

        tokenSequence.consume(TokenType.WHILE, "需要while");
        tokenSequence.consume(TokenType.LEFT_PAREN, "需要(");

        Expression condition = expressionParser.parse();
        tokenSequence.consume(TokenType.LEFT_PAREN, "需要)");

        return new DoWhileCode(condition, statement);
    }

    private Code forCode() {
        tokenSequence.advance();
        tokenSequence.consume(TokenType.LEFT_PAREN, "需要(");
        Statement preLoop = statementParser.parse();
        tokenSequence.consume(TokenType.SEMICOLON, "需要(");
        Expression condition = expressionParser.parse();
        tokenSequence.consume(TokenType.SEMICOLON, "需要(");

        Statement postEachIteration = statementParser.parse();
        Statement forStatement = statementParser.parse();
        return new ForCode(preLoop, condition, postEachIteration, forStatement);
    }

    private Code whileCode() {
        tokenSequence.advance();
        tokenSequence.consume(TokenType.LEFT_PAREN, "需要(");
        Expression whileCondition = expressionParser.parse();
        tokenSequence.consume(TokenType.RIGHT_PAREN, "需要)");
        Statement whileStatement = statementParser.parse();
        return new WhileCode(whileCondition, whileStatement);
    }


    private Code statementCode() {
        return new StatementCode(statementParser.parse());
    }

    private Code ifCode() {
        tokenSequence.advance();

        tokenSequence.consume(TokenType.LEFT_PAREN, "if 后需要存在 (");
        Expression ifCondition = expressionParser.parse();
        tokenSequence.consume(TokenType.RIGHT_PAREN, "需要 )");
        Statement ifStatement = statementParser.parse();

        List<IfCode.ElseIfCode> elseIfCodeList = new ArrayList<>();
        while (tokenSequence.curEqual(TokenType.ELSEIF)) {
            tokenSequence.advance();
            tokenSequence.consume(TokenType.LEFT_PAREN, "elseif 后需要存在 (");
            Expression elseIfCondition = expressionParser.parse();
            tokenSequence.consume(TokenType.RIGHT_PAREN, "需要 )");
            Statement elseIfStatement = statementParser.parse();
            elseIfCodeList.add(new IfCode.ElseIfCode(elseIfCondition, elseIfStatement));
        }

        Statement elseStatement = statementParser.parse();
        return new IfCode(ifCondition, ifStatement, elseIfCodeList, elseStatement);
    }

    private void synchronize() {
        tokenSequence.advance();
        while (!tokenSequence.isAtEnd()) {
            if (tokenSequence.curEqual(TokenType.SEMICOLON)) {
                tokenSequence.advance();
                return;
            }
            switch (tokenSequence.takeCur().getType()) {
                case CLASS:
                case FUN:
                case VAR:
                case FOR:
                case IF:
                case WHILE:
                case PRINT:
                case RETURN:
                    return;
            }
            tokenSequence.advance();
        }
    }

}